﻿/*************************************************************************************
 * 工    具：  HK_SlidePage2D
 * 
 * 描    述：  2D轮播图工具，支持循环和往返两种切换模式。功能包含：
 *             自动轮播与手动拖动切换
 *             页码按钮联动
 *             左右切换按钮控制
 *             平滑动画过渡（基于DOTween）
 * 
 * 版    本：  V1.2
 * 作    者：  京产肠饭、谭潇杰
 * 
 * 创    建：  2021/12/23  V1.0
 *
 * 更    新：  2025/04/22  V1.1
 *            1.代码优化：优化变量命名，重构方法，移除冗余代码，增强代码可读性
 *            2.Bug修复：增加拖拽阈值，修复页面拖拽时误触的的BUG
 *            3.功能新增：支持切页自定义动画时长和缓动类型
 *            4.功能新增：新增两个public方法，供外界调用，可通过代码进行“上一页”和“下一页”的切换
 *
 *            2025/05/14  V1.2
 *            1.Bug修复：修复Loop模式下，循环到最后一页后，切换回第一页时，动画异常的BUG
 * 
 * 链    接：  https://gitee.com/JingChanChangFan/hk_-unity-tools
*************************************************************************************/

using System.Collections.Generic;
using UnityEngine.EventSystems;
using UnityEngine;
using UnityEngine.UI;
using DG.Tweening;

namespace HKTools
{
    [RequireComponent(typeof(RectTransform))]
    public class HK_SlidePage2D : MonoBehaviour, IPointerDownHandler, IPointerUpHandler
    {
        public enum MoveType
        {
            Loop,         //循环
            PingPong,     //来回往复
        }

        public enum MoveDir
        {
            Left,
            Right,
        }

        [Header("切换方式："), SerializeField] MoveType moveType = MoveType.PingPong;

        [Header("图集父物体："), SerializeField] RectTransform content;
        GridLayoutGroup grid;

        [Header("上一页按钮："), SerializeField] Button btn_LastPage;
        [Header("下一页按钮："), SerializeField] Button btn_NextPage;


        [Header("页码单选ToggleGroup："), SerializeField]
        ToggleGroup pageToggleGroup;
        List<Toggle> pageToggleList;

        [Header("自动轮播时长："), SerializeField, Range(0.1f, 30f)] float pageStayTime = 2.0f;

        [Header("是否自动轮播："), SerializeField] bool isAutoSlide = false;

        [Header("是否允许拖动切页："), SerializeField] bool isAllowDrag = true;
        bool isDragging = false;

        [Header("是否启用切页动画："), SerializeField] bool isUseAnim = false;
        bool isAniming;
        bool IsAniming
        {
            get => isAniming;
            set => isAniming = value && isUseAnim; // 当禁用动画时强制为 false
        }

        [Header("切页动画时长："), SerializeField, Range(0.1f, 2f)] float animTime = 1f;

        [Header("切页动画类型："), SerializeField] Ease animEase = Ease.Linear;

        // 自动轮播方向，-1表示向左，1表示向右
        MoveDir moveDir = MoveDir.Right;

        // 最大页码
        int maxPageIndex = 0;
        // 当前显示页的页码，下标从0开始
        int curPageIndex = 0;
        public int CurPageIndex
        {
            get { return curPageIndex; }
            set
            {
                // 新增数值变化检查
                if (value == curPageIndex) return;

                int newIndex = value;

                if (newIndex <= 0)
                {
                    newIndex = 0;
                    btn_LastPage.gameObject.SetActive(false);
                    btn_NextPage.gameObject.SetActive(true);
                }
                else if (newIndex >= maxPageIndex)
                {
                    if (moveType == MoveType.PingPong)
                        newIndex = maxPageIndex;

                    btn_LastPage.gameObject.SetActive(true);
                    btn_NextPage.gameObject.SetActive(false);
                }
                else
                {
                    btn_LastPage.gameObject.SetActive(true);
                    btn_NextPage.gameObject.SetActive(true);
                }

                // 再次检查最终数值是否变化
                if (newIndex == curPageIndex) return;

                curPageIndex = newIndex;
                SwitchToPageNum();
            }
        }

        float recordTime = 0f;

        List<float> childItemPosX_List = new List<float>();

        //如果是Loop 判定当前是否已经实例化出来
        bool isAddChild = false;

        void Awake()
        {
            InitGridLayout();
            InitButtonAndToggle();

            CurPageIndex = 0;
        }

        /// <summary>
        /// 初始化GridLayout
        /// </summary>
        void InitGridLayout()
        {
            grid = content.GetComponent<GridLayoutGroup>();
            float cellSizeX = grid.cellSize.x;
            float spacingX = grid.spacing.x;

            childItemPosX_List.Clear();
            childItemPosX_List.Add(-cellSizeX * 0.5f);

            int childCount = content.transform.childCount;
            for (int i = 1; i <= childCount; i++)
            {
                if (moveType != MoveType.Loop && i == childCount)

                    continue;
                childItemPosX_List.Add(childItemPosX_List[i - 1] - (cellSizeX + spacingX));
            }
        }

        /// <summary>
        /// 初始化按钮和Toggle
        /// </summary>
        void InitButtonAndToggle()
        {
            btn_LastPage.onClick.AddListener(() =>
            {
                SwitchToLastPage();
            });

            btn_NextPage.onClick.AddListener(() =>
            {
                SwitchToNextPage();
            });

            if (pageToggleGroup.transform.childCount <= 0)
                return;

            pageToggleList = new List<Toggle>();
            for (int i = 0; i < pageToggleGroup.transform.childCount; i++)
            {
                var toggle = pageToggleGroup.transform.GetChild(i).GetComponent<Toggle>();
                if (toggle == null) continue;

                pageToggleList.Add(toggle);
                int pageNum = i; // 缓存当前页码
                toggle.onValueChanged.AddListener(isOn =>
                {
                    if (isOn && EventSystem.current.currentSelectedGameObject == toggle.gameObject)
                    {
                        CurPageIndex = pageNum;
                    }
                });
            }
            maxPageIndex = pageToggleList.Count - 1;
        }

        void Update()
        {
            if (!isAutoSlide || isDragging) return;

            if (Time.time > recordTime + pageStayTime)
            {
                recordTime = Time.time;

                // 如果是PingPong运动模式，则在运动到两头的时候改变方向
                if (moveType == MoveType.PingPong)
                {
                    if (CurPageIndex == 0)
                        moveDir = MoveDir.Right;
                    else if (CurPageIndex == maxPageIndex)
                        moveDir = MoveDir.Left;
                }

                CurPageIndex += (moveDir == MoveDir.Left) ? -1 : 1;
            }
        }

        /// <summary>
        /// 重置到第一页
        /// </summary>
        /// <param name="isImmediately">重置时，是否沿用切页动画</param>  
        public void ResetPage(bool isImmediately)
        {
            bool isUseAnimTemp = isUseAnim;

            if (isImmediately && isUseAnim)
            {
                isUseAnim = false;
            }

            CurPageIndex = 0;
            isUseAnim = isUseAnimTemp;
        }

        /// <summary>
        /// 切换至上一页
        /// </summary>
        public void SwitchToLastPage()
        {
            CurPageIndex--;
        }

        /// <summary>
        /// 切换至下一页
        /// </summary>
        public void SwitchToNextPage()
        {
            CurPageIndex++;
        }

        /// <summary>
        /// 切换至某页
        /// </summary>
        void SwitchToPageNum()
        {
            Vector3 pos = content.localPosition;

            if (moveType == MoveType.Loop && CurPageIndex == maxPageIndex)
                AddChildTempItem();

            if (isUseAnim)
            {
                if (moveType == MoveType.Loop && CurPageIndex > maxPageIndex)  //如果移动到最后
                {
                    IsAniming = true;
                    content.DOLocalMoveX(childItemPosX_List[CurPageIndex], animTime).SetEase(animEase)
                        .OnComplete(() =>
                        {
                            content.localPosition = new Vector3(childItemPosX_List[0], pos.y, pos.z);
                            IsAniming = false;
                            CurPageIndex = 0;
                        });
                }
                else
                {
                    IsAniming = true;
                    content.DOLocalMoveX(childItemPosX_List[CurPageIndex], animTime).SetEase(animEase)
                        .OnComplete(() => IsAniming = false);
                }
            }
            else
            {
                if (moveType == MoveType.Loop && CurPageIndex > maxPageIndex) //移动到最后
                {
                    content.localPosition = new Vector3(childItemPosX_List[0], pos.y, pos.z);
                    CurPageIndex = 0;
                }
                else
                {
                    content.localPosition = new Vector3(childItemPosX_List[CurPageIndex], pos.y, pos.z);
                }
            }

            recordTime = Time.time;//重新计时
            pageToggleList[CurPageIndex > maxPageIndex ? 0 : CurPageIndex].isOn = true;
        }

        void AddChildTempItem()
        {
            if (!isAddChild)
            {
                Instantiate(content.GetChild(0).gameObject, content.transform);
                isAddChild = true;
            }
        }

        Vector3 originDragPos = Vector3.zero;

        public void OnPointerDown(PointerEventData eventData)
        {
            if (!isAllowDrag || IsAniming || eventData.button != PointerEventData.InputButton.Left)
                return;

            isDragging = true;
            originDragPos = eventData.position;
        }

        public void OnPointerUp(PointerEventData eventData)
        {
            if (!isAllowDrag || IsAniming || !isDragging || eventData.button != PointerEventData.InputButton.Left)
                return;

            // 新增阈值判断
            float deltaX = eventData.position.x - originDragPos.x;

            if (Mathf.Abs(deltaX) < 30) // 拖动阈值
            {
                isDragging = false;
                return;
            }

            // 优化方向判断逻辑
            CurPageIndex += deltaX > 0 ? -1 : 1;

            isDragging = false;
        }
    }
}